/*
 *
 *
 * author:dustin
 * email:dushaobindoudou@gmail.com
 *
 *
 *
 *
 * 这个验证插件是这样的指定一个form表单
 * 里面只需要有如下的结构就可以了
 *
 * <input name="email" data-validate="vType=requried,when=blur,fail=email不正确,success=不错哦;vType=email,when=blur,fail=密码不能为空&setClass:fail，success=setClass:show;" />
 *
 * 设置说明：data-validate这个属性标明要验证的格式
 * 每个验证用分号隔开（注意英文的哦），vType表示要验证的类型，which表示那个元素触发（默认是自己）， when表示什么事件会触发这个判定，fail表示验证失败会出现什么提示这里&表示and，可以有多个操作，
 * setClass表示要设置的类用:分开后跟类名，removeClass表示移除类表示你要移除哪个类，这里可以添加html代码，但是不建议添加，这样会使你的html看起来非常混乱，基本上够了
 *
 * 说明:
 * 1 class类中不能使用-，可以使用下划线(_)
 * 2 验证的表单框，禁止使用onclick=functino这样绑定事件（!importent）
 * 3 addClass removeClass 里面的样式不要相同，否则后果自负
 * 4 这里只对submit做了处理，如果是ajax提交需要自己写了哦，这里提供ajax接口
 * 5 如果验证一组，则可以通过参数传入该组信息进行分析
 * 6 如果要在框的box要改变，那就传参数吧，然后修改方法
 * 7 支持多表单，支持多信息提示，比如验证密码的复杂度，每个复杂度定义一个方法，这样就行了，这样能满足大部分情况，支持ajax验证，ajax提交信息，都需要根据具体情况添加你自己的方法了
 * 8 validFun，是写验证方法的地方，会自动分配不同方法到不同元素的事件上
 *
 *
 * addForm参数是： {id:formid,which:"self",when:"click"}
 *
 * 生成后的结构是：{id:formid,ele:form,whichEle:"self",when:""}
 *
 * 生成事件队列是很有必要的，每个元素的每个事件要有一个验证队列以便于管理
 *
 * form 数据结构不合理存在问题，需要重新设计，因为如果一个验证失败了下面的验证还会继续，下面的验证结构会覆盖上面的验证结果，不合理
 *
 * 应改为队列，每个元素的每个验证事件都要有一个事件队列如果一个失败了，就取消后面要执行的验证同时记录每个失败的验证最后统一整理
 * which,相应的改为要验证哪个元素，而不是由哪个元素来验证，否则就杯具了config，是写配置信息的地方，正则表达式都在这里
 *
 *
 *
 * form:{
 *  ele:"",
 *  id:"",
 *  elems://所有涉及到的元素[
 *  	{   name:"",
 *  	    ele:"",
 *  		evt:{//所有事件
 *  	  		click:[vadaliteQuene，vadaliteQuene，vadaliteQuene，.....],
 *  	  		blue:[vadaliteQuene，vadaliteQuene，vadaliteQuene，.....],
 *  			mousedown:[vadaliteQuene，vadaliteQuene，vadaliteQuene，.....]
 *  	    }
 *  	},
 *  	...
 *
 *  ],
 *  which:"self",
 *  when:"click"
 * }
 *
 *
 * 生成的队列结构
 *
 * vadaliteQuene: [{
 *                name: {
 *                   ele: "元素",
 *                   "validateCondition": [{
 *                      "condition": "required",
 *                      "when": "click",
 *                      success:{
 *                      	message:"消息",
 *                      	removeClass:[]//class 列表
 *                      	addClass:[]//class列表
 *                      }，
 *                      fail:{
 *                      	message:"消息"，
 *                      	removeClass:[]//class 列表
 *
 *                      },
 *						isVd:"false",
 *						failedIsFoc:"false"
 *                   }
 *                   ....
 *                   ]
 *   	}
 * }]
 *
 *
 *
 */
;
(function(win){
    var doc = win.document;
    var extend = function(source, target){
        for (var k in source) {
            if (source.hasOwnProperty(k)) {
                target[k] = source[k];
            }
        }
        return target;
    }
    var config = {
        useableTag: ["input", "textarea", "select"],
        validateSin: "data-validate",
        analyseVal: {
            fail: /(?:,?\s*fail)\s*=[^,.]+/g,
            success: /(?:,?\s*success)\s*=[^,.]+/g,
            when: /(?:,?\s*when)\s*=([^,.]+)/g,
            which: /(?:,?\s*which)\s*=([^,.]+)/g,
            vType: /(?:,?\s*vType)\s*=([^,.]+)/g,
            addClass: /\&?\s*addClass\s*:\s*[\w\s]*\&?/g,
            removeClass: /\&?\s*removeClass\s*:\s*[\w\s]*\&?/g,
            params: /\(([^\(\)\.]*)\)/g
        },
        validRegx: {
            required: /\S/,
            number: /^[+-]?\d*\.?\d*$/g,
            email: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6}$/ig
        },
        subAndLeft: /^\s*&?\s*/g,
        subAndRight: /\s*&?\s*$/g,
        trimLeft: /^\s*/g,
        trimRight: /\s*$/g
    }
    
    
    var validFun = {
        required: function(res, tip){
            var that = this;
            //设置验证消息框
            if (!tip) {
                tip = createSpan();
            }
            res.which.parentNode.appendChild(tip, res.which);
            var theVal = res.which.value;
            if (theVal && theVal.length > 0) {//处理成功的情况
                processResult(tip, res.success);
                res.isValidated = true;
                return true;
            }
            else {//处理失败的时候
                processResult(tip, res.fail);
                res.isValidated = false;
                return false;
            }
        },
        email: function(res, tip){
            var that = this;
            //设置验证消息框
            if (!tip) {
                tip = createSpan();
            }
            res.which.parentNode.appendChild(tip, res.which);
            var theVal = res.which.value && (config.validRegx.email.test(res.which.value));
            config.validRegx.email.lastIndex = 0;
            if (theVal) {//处理成功的情况
                processResult(tip, res.success);
                return true;
            }
            else {//处理失败的时候
                processResult(tip, res.fail);
                return false;
            }
            
        },
        number: function(res, tip){
            var that = this;
            //设置验证消息框
            if (!tip) {
                tip = createSpan();
            }
            res.which.parentNode.appendChild(tip, res.which);
            var theVal = res.which.value && (config.validRegx.number.test(res.which.value));
            
            config.validRegx.number.lastIndex = 0;
            if (theVal) {//处理成功的情况
                processResult(tip, res.success);
                return true;
            }
            else {//处理失败的时候
                processResult(tip, res.fail);
                return false;
            }
        },
        len: function(res, tip){
            var theVal = res.which.value, theValLen = theVal.length;
            var theParams = res.params;
            var theLt = /\<(\d+)/.exec(theParams);
            var lt = theLt ? parseFloat(theLt[1]) : 0;
            var theMl = theParams.match(/\>(\d+)/);
            var mt = theMl ? parseFloat(theMl[1]) : 0;
            if (!tip) {
                tip = createSpan();
            }
            res.which.parentNode.appendChild(tip, res.which);
            if (theValLen) {
                //小于，大于
                if (lt && mt) {
                    if (theValLen < lt && theValLen > mt) {
                        processResult(tip, res.success);
                        return true;
                    }
                    else {
                        processResult(tip, res.fail);
                        return false;
                    }
                }
                if (lt) {
                    if (theValLen < lt) {
                        processResult(tip, res.success);
                        return true;
                    }
                    else {
                        processResult(tip, res.fail);
                        return false;
                    }
                }
                if (mt) {
                    if (theValLen > mt) {
                        processResult(tip, res.success);
                        return true;
                    }
                    else {
                        processResult(tip, res.fail);
                        return false;
                    }
                }
                
            }
        }
    }
    
    var processResult = function(span, result){
        if (!span || !result) {
            return;
        }
        var i = 0, len;
        //设置message
        result.message && (span.innerHTML = result.message);
        //处理添加class
        if (result.addClass) {
            len = result.addClass.length;
            for (; i < len; i++) {
                addClass(span, result.addClass[i]);
            }
        }
        //处理删除class
        i = 0;
        if (result.removeClass) {
            len = result.removeClass.length;
            for (; i < len; i++) {
                removeClass(span, result.removeClass[i]);
            }
        }
        
    }
    
    var hasClass = function(ele, cls){
        return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
    }
    
    var addClass = function(ele, cls){
        if (!hasClass(ele, cls)) {
            ele.className += " " + cls;
        }
    }
    
    var replaceClass = function(ele, oldClass, newClass){
        if (hasClass(ele, oldClass)) {
            removeClass(ele, oldClass);
            addClass(ele, newClass);
        }
        return;
    }
    
    var removeClass = function(ele, cls){
        if (hasClass(ele, cls)) {
            var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
            ele.className = ele.className.replace(reg, ' ');
        }
    }
    
    var ajax = (function(){
        // 兼容不同浏览器的 Adapter 适配层
        if (typeof window.XMLHttpRequest === "undefined") {
            window.XMLHttpRequest = function(){
                return new window.ActiveXObject(navigator.userAgent.indexOf("MSIE 5") >= 0 ? "Microsoft.XMLHTTP" : "Msxml2.XMLHTTP");
            };
        }
        
        
        var ajax = function(uri, options){
            var httpRequest, httpSuccess, timeout, isTimeout = false, isComplete = false;
            
            options = {
                method: options.method || "GET",
                data: options.data || null,
                arguments: options.arguments || null,
                
                onSuccess: options.onSuccess ||
                function(){
                },
                onError: options.onError ||
                function(){
                },
                onComplete: options.onComplete ||
                function(){
                },
                //尚未测试
                onTimeout: options.onTimeout ||
                function(){
                },
                isAsync: options.isAsync || true,
                timeout: options.timeout ? options.timeout : 10000,
                contentType: options.contentType ? options.contentType : "utf-8",
                type: options.type || "xml"
            };
            uri = uri || "";
            timeout = options.timeout;
            
            
            httpRequest = new window.XMLHttpRequest();
            httpRequest.open(options.method, uri, options.isAsync);
            //设置编码集
            //httpRequest.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
            httpRequest.setRequestHeader("Content-Type", options.contentType);
            
            /**
             * @ignore
             */
            httpSuccess = function(r){
                try {
                    return (!r.status && location.protocol == "file:") ||
                    (r.status >= 200 && r.status < 300) ||
                    (r.status == 304) ||
                    (navigator.userAgent.indexOf("Safari") > -1 && typeof r.status == "undefined");
                } 
                catch (e) {
                
                }
                return false;
            }
            
            
            httpRequest.onreadystatechange = function(){
                if (httpRequest.readyState == 4) {
                    if (!isTimeout) {
                        var o = {};
                        o.responseText = httpRequest.responseText;
                        o.responseXML = httpRequest.responseXML;
                        o.uri = uri;
                        o.arguments = options.arguments;
                        
                        if (httpSuccess(httpRequest)) {
                            if (options.type === "script") {
                                eval.call(window, data);
                            }
                            options.onSuccess(o);
                            
                        }
                        else {
                            options.onError(o);
                        }
                        options.onComplete(o);
                    }
                    isComplete = true;
                    //删除对象,防止内存溢出
                    httpRequest = null;
                }
            };
            
            httpRequest.send(options.data);
            
            window.setTimeout(function(){
                var o;
                if (!isComplete) {
                    isTimeout = true;
                    o = {};
                    o.uri = uri;
                    o.arguments = options.arguments;
                    options.onTimeout(o);
                    options.onComplete(o);
                }
            }, timeout);
            
            return httpRequest;
        };
        
        return ajax;
        
    })();
    
    
    var trimStr = function(str){
        return str.replace(config.trimLeft, "").replace(config.trimRight, "");
    }
    var subAndSp = function(str){
        return str.replace(config.subAndLeft, "").replace(config.subAndRight, "");
    }
    
    var bind = function(fun, context){
        if (arguments.length < 2 && context === undefined) {
            return fun;
        }
        var slice = Array.prototype.slice, args = slice.call(arguments, 2);
        return function(){
            var array = slice.call(arguments, 0);
            return fun.apply(context, args.concat(array));
        }
    }
    
    var bindEvent = function(ele, evt, handler){
        if (ele.addEventListener) {
            ele.addEventListener(evt, handler, false);
        }
        else 
            if (ele.attachEvent) {
                ele.attachEvent("on" + evt, handler);
            }
            else {
                ele["on" + evt] = handler;
            }
    }
    
    var unbindEvent = function(ele, evt, hander){
        if (ele.removeEventListener) {
            ele.removeEventListener(evt, hander, false);
        }
        else 
            if (ele.detachEvent) {
                ele.detachEvent("on" + evt, hander);
            }
            else {
                ele["on" + evt] = null;
            }
    }
    
    var stopEvent = function(e){
        if (!e) 
            var e = window.event;
        
        //e.cancelBubble is supported by IE - this will kill the bubbling process.
        e.cancelBubble = true;
        e.returnValue = false;
        
        //e.stopPropagation works only in Firefox.
        if (e.stopPropagation) {
            e.stopPropagation();
            e.preventDefault();
        }
        return false;
    }
    
    var createSpan = function(){
        var span = doc.createElement("span");
        return span;
    }
    
    var getAttr = function(ele, attr){
        if (!ele) {
            return;
        }
        if (ele.getAttribute) {
            return ele.getAttribute(attr);
        }
        else {
            return ele[attr];
        }
        //ie7- class 注意    
    }
    
    
    var Validator = function(){
        var args = Array.prototype.slice.call(arguments);
        if (this === win) {
            return new Validator(args.join(","));
        }
        this.formList = [];
        this.validatorList = [];
        
        return this;
        
    }
    Validator.prototype = {
        constructor: Validator,
        addForm: function(form){
            var target = {
                which: "self",
                when: "click"
            };
            if (form) {
                target = extend(form, target);
                this.addFormToList(target);
            }
            return this;
        },
        addFormToList: function(form){
            var i = 0, len = this.formList.length, formed;
            if (!form.id) {
                return;
            }
            for (; i < len; i++) {
                if (form.id == this.formList[i].id) {
                    return;
                }
            }
            //form which
            form.which = this.processWhich(form.which);
            
            //todo:生成验证框列表
            form.validateList = this.genterValidatorList(form);
            //把生成好的列表放入表单列表中
            bindEvent(form.ele, form.when, bind(this.validateAll, this, form));
            this.formList.push(form);
            
        },
        
        genterValidatorList: function(form){
            form.ele = doc.getElementById(form.id);
            if (form.ele) {
                //得到所有要验证的元素元素        
                var inputList = this.getInputList(form.which);
                //todo:验证列表是否已经存在
                return inputList;
            }
            return [];
        },
        getInputList: function(form){
            var list = [], i = 0, j = 0, len = config.useableTag.length, inputs, inputsLen = 0, name = "", lastRes;
            
            for (; i < len; i++) {
                inputs = form.getElementsByTagName(config.useableTag[i]);
                inputsLen = inputs.length;
                j = 0;
                for (; j < inputsLen; j++) {
                    lastRes = this.processInput(inputs[j]);
                    if (lastRes) 
                        list.push(lastRes);
                }
            }
            return list;
        },
        processInput: function(input){
            //var dataVali;
            var dataVali = getAttr(input, config.validateSin);
            var name = getAttr(input, "name");
            var eleCon = {};
            if (dataVali && dataVali.length > 1) {
                //todo:标明这个元素需要验证,解析验证信息
                eleCon.name = name;
                eleCon.source = input;
                if (!eleCon.tip) {
                    eleCon.tip = createSpan();
                }
                eleCon.validateCondition = this.analyseVali(dataVali, input, eleCon.tip);
                return eleCon;
            }
            return false;
        },
        analyseVali: function(dataVali, ele, tip){
            var vtList = dataVali.split(";"), res = [], i = 0, len = vtList.length, sgle = "", anysVal, fnlRes = {};
            for (; i < len; i++) {
                sgle = vtList[i];
                if (sgle && sgle.length > 1) {
                    anysVal = this.anasyTheVal(sgle, ele);
                    if (fnlRes[anysVal.when]) {
                        fnlRes[anysVal.when].push(anysVal);
                    }
                    else {
                        fnlRes[anysVal.when] = [];
                        fnlRes[anysVal.when].push(anysVal);
                    }
                }
            }
            //开始分配相应的的函数到相应的事件上
            //绑定相应的函数
            this.dispatcher(fnlRes, ele, tip);
            //队列{element,event,caozuo}
            return fnlRes;
            
        },
        anasyTheVal: function(sgle, ele){
            var res = {}, tempWhich = "";
            //todo:这里可以来一个循环
            
            //解析执行的事件
            res.when = this.getVdVal(sgle, "when");
            //解析验证类型
            res.vType = this.getVdVal(sgle, "vType");
            var params = config.analyseVal.params.exec(res.vType);
            config.analyseVal.params.lastIndex = 0;
            if (params) {
                res.params = params[1];
            }
            else {
                res.params = "";
            }
            res.vType = res.vType.replace(config.analyseVal.params, "");
            config.analyseVal.params.lastIndex = 0;
            /*
             * todo: 绑定指定的函数
             *
             */
            //解析绑定到那个元素上            
            //todo:which 应该得到具体是那个元素，处理self
            res.which = this.processWhich(this.getVdVal(sgle, "which")) || ele;
            //解析失败时的消息动作
            var failTemp = this.getVdVal(sgle, "fail");
            res.fail = {};
            res.fail.addClass = failTemp ? this.anasyMsg(failTemp, "addClass") : [];
            res.fail.removeClass = failTemp ? this.anasyMsg(failTemp, "removeClass") : [];
            res.fail.message = failTemp ? failTemp.replace(config.analyseVal.addClass, "").replace(config.analyseVal.removeClass, "") : "";
            //解析成功时候的消息动作
            var succTemp = this.getVdVal(sgle, "success");
            res.success = {};
            res.success.addClass = succTemp ? this.anasyMsg(succTemp, "addClass") : [];
            res.success.removeClass = succTemp ? this.anasyMsg(succTemp, "removeClass") : [];
            res.success.message = succTemp ? succTemp.replace(config.analyseVal.addClass, "").replace(config.analyseVal.removeClass, "") : "";
            //是否验证过
            res.isValidated = false;
            //todo:增加是验证失败时候是否自动获取焦点     name(   ) ;            
            return res;
        },
        anasyMsg: function(msg, opt){
            var addCls = msg.match(config.analyseVal[opt]), i = 0, len = addCls ? addCls.length : 0;
            var clss = [];
            for (; i < len; i++) {
                if (addCls[i]) {
                    //console.log(addCls[i]);
                    clss.push(subAndSp(addCls[i].split(":")[1]));
                }
            }
            return clss;
        },
        dispatcher: function(res, source, tip){
            var handler;
            var that = this;
            var theVfun = function(res, tip){
                if (!res) {
                    return;
                }
                var i = 0;
                len = res.length;
                for (; i < len; i++) {
                    if (!validFun[res[i].vType].call(this, res[i], tip)) {
                        return false;
                    }
                }
            };
            //todo:这个要检验一下是否支持该事件
            //生成事件队列，一个验证错误，验证将不能再向下运行
            for (var k in res) {
                if (res.hasOwnProperty(k)) {
                    hander = k.toLowerCase();
                    bindEvent(source, k, bind(theVfun, that, res[k], tip));
                }
            }
        },
        getVdVal: function(sgle, opt){
            var temp = sgle.match(config.analyseVal[opt]);
            config.analyseVal[opt].lastIndex = 0;
            return temp ? (trimStr(temp[0].split("=")[1])) : "";
            
        },
        processWhich: function(which){
            if (!which) {
                return false;
            }
            else {
                if (which.toLowerCase() == "self") {
                    return false;
                }
                else {
                    return doc.getElementById(which);
                }
            }
        },
        validateAll: function(form){
            var failList = [];
            var that = this;
            var theVfun = function(res, tip){
                if (!res) {
                    return;
                }
                var i = 0;
                var len = res.length;
                for (; i < len; i++) {
                    if (!validFun[res[i].vType].call(that, res[i], tip)) {
                        //验证失败
                        failList.push({
                            "name": res.which,
                            isValited: "false",
                            vc: res
                        });
                        return false;
                    }
                }
            };
            //就在这里吧
            var i = 0, len = form.validateList.length, vl, j;
            for (; i < len; i++) {
                vl = form.validateList[i];
                for (j in vl.validateCondition) {                
                    if (vl.validateCondition.hasOwnProperty(j)) {                    
                        theVfun(vl.validateCondition[j], vl.tip);
                    }
                }
            }
            //验证不通过
            if (failList.length > 0) {
                return stopEvent(arguments[1]);
            }
            //验证通过
        }
    }
    
    
    win.validator = Validator();
})(window)

